#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>

#include "s_qspi_drv.h"
#include "s_qspi_reg.h"

static int major = 0;
static struct class *qspi_class;
volatile int g_int_flag = 0;

/* todo list:
	1. add device-tree for each channel interruption in \peta_proj\axi_qspi\project-spec\meta-user\recipes-bsp\device-tree\files\system-user.dtsi
	2. rebuild all and pack the BOOT.bin and image.ub
	3. add device-tree path for each channel in s_qspi_reg.h
	4. request_irq when open and init a channel.
	5. free_irq when close a channel.
	6. free_irq when this module exit.
*/

/*
unsigned int g_dev_base_list[DEV_NUM] = {
	DEV_QSPI_00_BASE_ADDR, DEV_QSPI_01_BASE_ADDR, DEV_QSPI_02_BASE_ADDR, DEV_QSPI_03_BASE_ADDR, DEV_QSPI_04_BASE_ADDR,
	DEV_QSPI_05_BASE_ADDR, DEV_QSPI_06_BASE_ADDR, DEV_QSPI_07_BASE_ADDR, DEV_QSPI_08_BASE_ADDR, DEV_QSPI_09_BASE_ADDR,
	DEV_QSPI_10_BASE_ADDR, DEV_QSPI_11_BASE_ADDR, DEV_QSPI_12_BASE_ADDR, DEV_QSPI_13_BASE_ADDR, DEV_QSPI_14_BASE_ADDR,
	DEV_QSPI_15_BASE_ADDR};
*/
unsigned int g_dev_base_list[DEV_NUM] = {DEV_QSPI_00_BASE_ADDR};
unsigned int g_list_top_buf_addr[DEV_NUM] = {TOP_LEVEL_BUF_00_ADDR};
const char *g_list_irq_dt_path[DEV_NUM] = {QSPI_00_IRQ_DT_PATH}; 
char g_list_irq_2_minor[256] = {0};
char g_list_minor_2_irq[DEV_NUM] = {0};

/* virtual address of registers */
S_QSPI_INFO_st g_list_dev_info[DEV_NUM];

irqreturn_t qspi_isr(int irq, void *dev_id)
{
	int minor = 0;
	int reg_val = 0;
	//get minor num from irq .g_list_irq_2_minor is a mapping table from irq to minor.
//	g_int_flag = 1;
	printk("we get interrupt irq %d\n",irq);
//	return IRQ_HANDLED;
	minor = (int)g_list_irq_2_minor[irq];
	//query if interruption is this device.for now , we give each channel their own interrupt line.
	//here we get the interruption
	reg_val = readl(g_list_dev_info[minor].qspi_int_status_reg);
	if (reg_val & NODE_INT)
	{
		//clear the interrupt register
		writel(CLR_NODE_INT, g_list_dev_info[minor].qspi_int_clear_reg);
		//set the interrupt flag
		g_list_dev_info[minor].ev_occured = 1;
		wake_up_interruptible(&(g_list_dev_info[minor].dev_wait_queue));
	}

	return IRQ_HANDLED;
}

//init qspi device by minor
static int device_open_init(int minor)
{
	int i = 0;
	char s_int_name[32] = {0};
	struct device_node *nd;
	int irq_num = 0;
	int err = 0;

	if ((minor < 0) || (minor > (DEV_NUM - 1)))
	{
		printk("device minor %d out of range!\n", minor);
		return -EFAULT;
	}
	if (g_list_dev_info[minor].remap_flag == 0)
	{
		//memory remap for minor dev
		printk("the physical addr is %x", (g_dev_base_list[minor] + TX_G0_BASE_ADDR + TX_ADDR_OFFSET));
		g_list_dev_info[minor].tx_fifo_0_addr_reg = ioremap(g_dev_base_list[minor] + TX_G0_BASE_ADDR + TX_ADDR_OFFSET, 4);
		printk("the virtual addr is %p", (g_list_dev_info[minor].tx_fifo_0_addr_reg));
		g_list_dev_info[minor].tx_fifo_0_ctl_reg = ioremap(g_dev_base_list[minor] + TX_G0_BASE_ADDR + TX_CTL_OFFSET, 4);
		g_list_dev_info[minor].tx_fifo_0_stat_reg = ioremap(g_dev_base_list[minor] + TX_G0_BASE_ADDR + TX_STATUS_OFFSET, 4);
		for (i = 0; i < 32; i++)
		{
			g_list_dev_info[minor].tx_fifo_0_data_reg[i] = ioremap(g_dev_base_list[minor] + TX_G0_BASE_ADDR + TX_DATA_START_OFFSET + i * 4, 4);
		}

		g_list_dev_info[minor].tx_fifo_1_addr_reg = ioremap(g_dev_base_list[minor] + TX_G1_BASE_ADDR + TX_ADDR_OFFSET, 4);
		g_list_dev_info[minor].tx_fifo_1_ctl_reg = ioremap(g_dev_base_list[minor] + TX_G1_BASE_ADDR + TX_CTL_OFFSET, 4);
		g_list_dev_info[minor].tx_fifo_1_stat_reg = ioremap(g_dev_base_list[minor] + TX_G1_BASE_ADDR + TX_STATUS_OFFSET, 4);
		for (i = 0; i < 32; i++)
		{
			g_list_dev_info[minor].tx_fifo_1_data_reg[i] = ioremap(g_dev_base_list[minor] + TX_G1_BASE_ADDR + TX_DATA_START_OFFSET + i * 4, 4);
		}

		g_list_dev_info[minor].rx_addr_reg = ioremap(g_dev_base_list[minor] + RX_BASE_ADDR+ RX_ADDR_OFFSET, 4);
		g_list_dev_info[minor].rx_ctl_reg = ioremap(g_dev_base_list[minor] + RX_BASE_ADDR+ RX_CTL_OFFSET, 4);
		g_list_dev_info[minor].rx_stat_reg = ioremap(g_dev_base_list[minor] + RX_BASE_ADDR+ RX_STATUS_OFFSET, 4);
		g_list_dev_info[minor].rx_ecc_addr_reg = ioremap(g_dev_base_list[minor] + RX_BASE_ADDR+ RX_ADDR_ON_ECC_OFFSET, 4);
		g_list_dev_info[minor].rx_ecc_info_reg = ioremap(g_dev_base_list[minor] + RX_BASE_ADDR+ RX_INFO_ON_ECC_OFFSET, 4);
		g_list_dev_info[minor].rx_ecc_cnt_reg = ioremap(g_dev_base_list[minor] + RX_BASE_ADDR+ RX_ECC_CNT_OFFSET, 4);
		for (i = 0; i < 32; i++)
		{
			g_list_dev_info[minor].rx_data_reg[i] = ioremap(g_dev_base_list[minor] + RX_BASE_ADDR+ RX_DATA_START_OFFSET + i * 4, 4);
		}

		g_list_dev_info[minor].qspi_int_status_reg = ioremap(g_dev_base_list[minor] + INT_REG_BASE + INT_STATUS_OFFSET, 4);
		g_list_dev_info[minor].qspi_int_mask_reg = ioremap(g_dev_base_list[minor] + INT_REG_BASE + INT_MASK_OFFSET, 4);
		g_list_dev_info[minor].qspi_int_clear_reg = ioremap(g_dev_base_list[minor] + INT_REG_BASE + INT_CLEAR_OFFSET, 4);

		g_list_dev_info[minor].ev_occured = 0;
		init_waitqueue_head(&(g_list_dev_info[minor].dev_wait_queue));
		sprintf(s_int_name, "s_qspi_int%02d", minor);
		//warnning: we need to probe the irq num here. for we dont know the correct irq by hardware.
		nd = of_find_node_by_path("/amba_pl/irq_node@0"); // ls /proc/device-tree/amba_pl/irq_node@0/
		if(NULL == nd) {
			printk(KERN_ERR "Failed to get gpio node at /amba_pl/irq_node@0\n");
			return -EINVAL;
		}

		if (0 == g_list_minor_2_irq[minor])
		{
			irq_num = irq_of_parse_and_map(nd, 0);
			if(0 == irq_num) {
				printk(KERN_ERR "Failed to get irq_num\n");
				return -EINVAL;
			}
			printk("irq_num is %d\n",irq_num);
		}
		else{
			irq_num = g_list_minor_2_irq[minor];
		}
		
		err = request_irq(irq_num, qspi_isr,IRQF_TRIGGER_RISING| IRQF_ONESHOT,DEVICE_NAME,NULL);
		//err = request_threaded_irq(irq_num, NULL, qspi_isr, IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev_name, NULL);
		if (err)
		{
			printk(KERN_ALERT "request_irq error=%d\n", err);
			return -EINVAL;
		}
		else
		{
			printk("request_irq success ! \n");
		}
		g_list_irq_2_minor[irq_num] = (char)minor;
		g_list_minor_2_irq[minor] = irq_num;

		g_list_dev_info[minor].remap_flag = 1;

		printk("Success opened device %d !\n", minor);
		return 0;
	}
	else
	{
		printk("Device %d has been already opened!\n", minor);
		return 0;
	}
}

static int device_close_deinit(int minor)
{
	int i;
	if ((minor < 0) || (minor > (DEV_NUM - 1)))
	{
		printk("device minor %d out of range!\n", minor);
		return -EFAULT;
	}

	if (g_list_dev_info[minor].remap_flag == 1)
	{
		//memory unmap for minor dev
		iounmap(g_list_dev_info[minor].tx_fifo_0_addr_reg);
		iounmap(g_list_dev_info[minor].tx_fifo_0_ctl_reg);
		iounmap(g_list_dev_info[minor].tx_fifo_0_stat_reg);
		for (i = 0; i < 32; i++)
		{
			iounmap(g_list_dev_info[minor].tx_fifo_0_data_reg[i]);
		}

		iounmap(g_list_dev_info[minor].tx_fifo_1_addr_reg);
		iounmap(g_list_dev_info[minor].tx_fifo_1_ctl_reg);
		iounmap(g_list_dev_info[minor].tx_fifo_1_stat_reg);
		for (i = 0; i < 32; i++)
		{
			iounmap(g_list_dev_info[minor].tx_fifo_1_data_reg[i]);
		}

		iounmap(g_list_dev_info[minor].rx_addr_reg);
		iounmap(g_list_dev_info[minor].rx_ctl_reg);
		iounmap(g_list_dev_info[minor].rx_stat_reg);
		iounmap(g_list_dev_info[minor].rx_ecc_addr_reg);
		iounmap(g_list_dev_info[minor].rx_ecc_info_reg);
		iounmap(g_list_dev_info[minor].rx_ecc_cnt_reg);
		for (i = 0; i < 32; i++)
		{
			iounmap(g_list_dev_info[minor].rx_data_reg[i]);
		}
		iounmap(g_list_dev_info[minor].qspi_int_status_reg);
		iounmap(g_list_dev_info[minor].qspi_int_mask_reg);
		iounmap(g_list_dev_info[minor].qspi_int_clear_reg);

		free_irq(g_list_minor_2_irq[minor], NULL);
		g_list_dev_info[minor].remap_flag = 0;
		printk("Success closed device %d !\n", minor);
		return 0;
	}
	else
	{
		printk("Device %d has been already closed!\n", minor);
		return -1;
	}
}
static inline int get_user_data(MSG_st *p_msg_user, MSG_st *p_msg_krnl, char *data_buf_k)
{
	int ret = 0;
	//get struct data
	ret = copy_from_user(p_msg_krnl, p_msg_user, sizeof(MSG_st));
	if ((ret != 0) || (((p_msg_krnl->data_len) != 4) && ((p_msg_krnl->data_len) != 32) && ((p_msg_krnl->data_len) != 64) && ((p_msg_krnl->data_len) != 128)))
		return -EFAULT;
	printk("the user data len we got is %d \n", p_msg_krnl->data_len);
	//get payload data
	ret = copy_from_user(data_buf_k, (char *)(p_msg_krnl->p_data), p_msg_krnl->data_len);
	if (ret)
		return -EFAULT;
	printk("target_addr we got from user is 0x%08x \n", p_msg_krnl->addr);
	return 0;
}

static inline int read_chip_data(int minor, int addr, char *data_buf_k, int data_len, int clr_2bit_ecc, int indirect_mode, int ecc_retry_cnt)
{
	int read_ecc_retry = 0;
	unsigned int reg_val = 0;
	unsigned int dummy_cycle = 0;
	int i = 0;

	for (read_ecc_retry = 0; read_ecc_retry < ecc_retry_cnt; read_ecc_retry++)
	{
		//set read target address register
		writel(addr, g_list_dev_info[minor].rx_addr_reg);
		//set read control register,start read.
		if (addr < BUF_1K_ADDR_RANGE) 
		{	//read target address is 1k buffer.
			dummy_cycle = (READ_1K_DUMMY_CYCLE << RX_DUMMY_CYCLE_OFFSET);
		}
		else
		{
			dummy_cycle = (READ_TOP_DUMMY_CYCLE << RX_DUMMY_CYCLE_OFFSET);
		}
		
		reg_val = (data_len << RX_DATA_LEN_OFFSET) | (clr_2bit_ecc << RX_CLR_2BIT_ECC_OFFSET) |
				  dummy_cycle | (indirect_mode << RX_INDIRECT_MOD_OFFSET) |
				  (g_list_dev_info[minor].ecc_enable) | (RX_START_RCV);
		printk("we read address is 0x%08x , read control register value is 0x%08x\n ", addr, reg_val);
		writel(reg_val, g_list_dev_info[minor].rx_ctl_reg);
		//for now , if the addr is node address,we do not wait the return data, just wait the interrupt.
		if ((addr & TOP_ADDR_RANGE) != TOP_ADDR_RANGE)
		{
			return 0;
		}
		
		//here we wait the receive finish signal, for this might only takes 10 us or less, we loop reading the register.
		for (i = 0; i < MAX_RETRY_CNT; i++)
		{
			reg_val = readl(g_list_dev_info[minor].rx_stat_reg);
			if (reg_val & RX_FINISHED)
				break;
		}
		if (i == MAX_RETRY_CNT)
		{
			printk("read from the %d dev timeout after %d retries !\n", minor, i);
			continue;
		}
		if (reg_val & RX_DATA_ERR)
		{
			printk("read status shows that an ECC error occured on %d dev!\n", minor);
			continue;
		}
		//here we read regitsters data and return...
		for (i = 0; i < (data_len / 4); i++)
		{
			*(int *)(data_buf_k + i * 4) = readl(g_list_dev_info[minor].rx_data_reg[i]);
		}
		break;
	}
	if (i == ecc_retry_cnt)
	{
		printk("read error too many times! retry times: %d\n", ecc_retry_cnt);
		return -EFAULT;
	}
	return 0;
}

static inline int write_chip_data(int minor, int addr, char *data_buf_k, int data_len, int write_fifo_num, int ecc_retry_cnt)
{
	int write_ecc_retry = 0;
	int i = 0;
	for (write_ecc_retry = 0; write_ecc_retry < ecc_retry_cnt; write_ecc_retry++)
	{
		if (0 == write_fifo_num)
		{
			printk("we use fifo 0 to send data...\n");
			writel(addr, g_list_dev_info[minor].tx_fifo_0_addr_reg); //write address
			for (i = 0; i < (data_len / 4); i++)
			{
				writel(*(int *)(&data_buf_k[i * 4]), g_list_dev_info[minor].tx_fifo_0_data_reg[i]); //write datas
			}
			printk("write control register is 0x%08x\n", ((data_len << TX_DATA_LEN_OFFSET) | g_list_dev_info[minor].ecc_enable | TX_START_SEND));
			writel((data_len << TX_DATA_LEN_OFFSET) | g_list_dev_info[minor].ecc_enable | TX_START_SEND, g_list_dev_info[minor].tx_fifo_0_ctl_reg); //write control register
			break;																																	//we do not check ecc error for now, and will add handler here ...
		}
		else if (1 == write_fifo_num)
		{
			printk("we use fifo 1 to send data...\n");
			writel(addr, g_list_dev_info[minor].tx_fifo_1_addr_reg); //write address
			for (i = 0; i < data_len / 4; i++)
			{
				writel(*(int *)(&data_buf_k[i * 4]), g_list_dev_info[minor].tx_fifo_1_data_reg[i]); //write datas
			}
			writel((data_len << TX_DATA_LEN_OFFSET) | g_list_dev_info[minor].ecc_enable | TX_START_SEND, g_list_dev_info[minor].tx_fifo_1_ctl_reg); //write control register
			break;																																	//we do not check ecc error for now, and will add handler here ...
		}
		else
		{
			return -EFAULT;
		}
	}
	if (write_ecc_retry == ecc_retry_cnt)
	{
		printk("Send failed for there are too many ECC errors! we retried %d times! \n", write_ecc_retry);
		return -EBUSY; //too many ECC errors!
	}
	return 0;
}

static ssize_t qspi_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static ssize_t qspi_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	//int err;
	//char status;
	//struct inode *inode = file_inode(file);
	//int minor = iminor(inode);

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	//err = copy_from_user(&status, buf, 1);

	return 1;
}

static int qspi_drv_open(struct inode *node, struct file *file)
{
	int minor = iminor(node);
	int major = imajor(node);

	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	printk("we will open dev major %d minor %d\n", major, minor);

	return device_open_init(minor);
}

static int qspi_drv_close(struct inode *node, struct file *file)
{
	int minor = iminor(node);
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_close_deinit(minor);
	return 0;
}

static long qspi_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	MSG_st *msg = (MSG_st *)arg;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	int ret = 0;
	char data_buf_k[128] = {0};
	unsigned int fifo_0_status = 0;
	unsigned int fifo_1_status = 0;

	if ((minor < 0) || (minor > (DEV_NUM - 1)))
	{
		printk("device minor %d out of range!\n", minor);
		return -1;
	}

	switch (cmd)
	{
	case CMD_GET_FIFO_STAT: // if fifo 0 is available, msg.data = 1, otherwize msg.data = 0.
	{
		int result = 0;
		fifo_0_status = readl(g_list_dev_info[minor].tx_fifo_0_stat_reg);
		fifo_1_status = readl(g_list_dev_info[minor].tx_fifo_1_stat_reg);
		if (fifo_0_status & TX_FIFO_EMPTY)
		{
			result = 1;
		}
		else if (fifo_1_status & TX_FIFO_EMPTY)
		{
			result = 2;
		}
		else
		{
			result = 0;
		}

		ret = copy_to_user((msg->p_data), &result, sizeof(result));
		if (ret)
		{
			return -EFAULT;
		}
		break;
	}
	case CMD_SET_ECC_ENABLE: // enable or disable the ecc on tx and rx.
	{
		int val = 0;
		MSG_st msg_k;
		ret = get_user_data(msg, &msg_k, data_buf_k);
		val = *(int *)data_buf_k;
		if ((ret != 0) || ((val != 0) && (val != 1)))
		{
			return -EFAULT;
		}
		if (val == 0) //disable ECC
		{
			g_list_dev_info[minor].ecc_enable = 0;
		}
		else //enable ECC
		{
			g_list_dev_info[minor].ecc_enable = EN_ECC;
		}
		break;
	}
	
	case CMD_WRITE_DATA:
	{
		MSG_st msg_krnl;
		int retry_times = 0;
		printk("we will invoke get_user_data\n");
		if (get_user_data(msg, &msg_krnl, data_buf_k) != 0)
		{
			printk("TX data len is %d, target_addr is 0x%08x ,\n data_buf in kernel is %s .\n", msg_krnl.data_len, msg_krnl.addr, data_buf_k);
			return -EFAULT;
		}
		//try to get an available fifo to write.
		while (retry_times < MAX_RETRY_CNT)
		{
			fifo_0_status = readl(g_list_dev_info[minor].tx_fifo_0_stat_reg);
			fifo_1_status = readl(g_list_dev_info[minor].tx_fifo_1_stat_reg);
			printk("TX fifo state 0 is 0x%08x , reg is g_list_dev_info[%d].tx_fifo_0_stat_reg:%p", fifo_0_status, minor, g_list_dev_info[minor].tx_fifo_0_stat_reg);
			printk("TX fifo state 1 is 0x%08x , reg is g_list_dev_info[%d].tx_fifo_1_stat_reg:%p", fifo_1_status, minor, g_list_dev_info[minor].tx_fifo_1_stat_reg);
			if (fifo_0_status & TX_FIFO_EMPTY)
			{
				write_chip_data(minor, msg_krnl.addr, data_buf_k, msg_krnl.data_len, 0, msg_krnl.retry_limit);
				break;
			}
			else if (fifo_1_status & TX_FIFO_EMPTY)
			{
				write_chip_data(minor, msg_krnl.addr, data_buf_k, msg_krnl.data_len, 1, msg_krnl.retry_limit);
				break;
			}
			else //no available fifo , retry...
			{
				retry_times++;
			}
		}
		if (retry_times == MAX_RETRY_CNT)
		{
			return -EBUSY;
		}
		break;
	}

	case CMD_WRITE_FIFO_0:
	{
		MSG_st msg_krnl;
		if (get_user_data(msg, &msg_krnl, data_buf_k) != 0)
		{
			printk("TX data len is %d, target_addr is 0x%08x ,\n data_buf in kernel is %s .\n", msg_krnl.data_len, msg_krnl.addr, data_buf_k);
			return -EFAULT;
		}
		write_chip_data(minor, msg_krnl.addr, data_buf_k, msg_krnl.data_len, 0, msg_krnl.retry_limit);
		break;
	}
	case CMD_WRITE_FIFO_1:
	{
		MSG_st msg_krnl;
		if (get_user_data(msg, &msg_krnl, data_buf_k) != 0)
		{
			printk("TX data len is %d, target_addr is 0x%08x ,\n data_buf in kernel is %s .\n", msg_krnl.data_len, msg_krnl.addr, data_buf_k);
			return -EFAULT;
		}
		write_chip_data(minor, msg_krnl.addr, data_buf_k, msg_krnl.data_len, 1, msg_krnl.retry_limit);
		break;
	}

	case CMD_READ_DATA:
	{
		MSG_st rcv_msg;
		int indirect_mode = 0;
		ret = copy_from_user(&rcv_msg, msg, sizeof(MSG_st));
		g_list_dev_info[minor].ev_occured = 0;
		//get the read mode, if address pointed to a node, it need a indirect read.(node -> top, top->spi)
		indirect_mode = (rcv_msg.addr & TOP_ADDR_RANGE) == TOP_ADDR_RANGE ? 0 : 1;

		if (indirect_mode)
		{
			//we send node addrs on first time, and we will send top level buffer address later.
			ret = read_chip_data(minor, rcv_msg.addr, data_buf_k, rcv_msg.data_len, 0, indirect_mode, rcv_msg.retry_limit);
			if (0 == ret)
			{
				//here we wait for the operation of transfer node data to top level ...
				printk("indirect_read : we are waiting for interrupt from the AP...\n");
				ret = wait_event_interruptible_timeout(g_list_dev_info[minor].dev_wait_queue, g_list_dev_info[minor].ev_occured, usecs_to_jiffies(1000)); //wait here ...

				if (ret >= 0)
				{
					if (g_list_dev_info[minor].ev_occured)
					{
						g_list_dev_info[minor].ev_occured = 0;
					}
					else
					{
						printk("wait event timeout!\n");
						return -ETIMEDOUT;
					}
				}
				else
				{
					printk("wait event interrupted by system signal!\n");
					return ret;
				}

				printk("we got interrupt from the AP, let's read from the top level buffer...\n");
				ret = read_chip_data(minor, g_list_top_buf_addr[minor], data_buf_k, rcv_msg.data_len, 0, 0, rcv_msg.retry_limit);
				if (0 == ret)
				{
					if (copy_to_user(rcv_msg.p_data, data_buf_k, rcv_msg.data_len))
					{
						return -EFAULT;
					}
					return 0;
				}
				else
				{
					return ret;
				}
			}
			else
			{
				return ret;
			}
		}
		else //direct read
		{
			ret = read_chip_data(minor, rcv_msg.addr, data_buf_k, rcv_msg.data_len, 0, 0, rcv_msg.retry_limit);
			if (0 == ret)
			{
				if (copy_to_user(rcv_msg.p_data, data_buf_k, rcv_msg.data_len))
				{
					return -EFAULT;
				}
				return 0;
			}
			else
			{
				return ret;
			}
		}
		break;
	} //end of case CMD_READ_DATA
	default:
		return -ENOTTY;
	}
	return 0;
}

static struct file_operations qspi_drv = {
	.owner = THIS_MODULE,
	.open = qspi_drv_open,
	.read = qspi_drv_read,
	.write = qspi_drv_write,
	.unlocked_ioctl = qspi_drv_ioctl,
	.release = qspi_drv_close,
};

static int __init qspi_init(void)
{
	int err;
	int i;
	//struct device_node *nd;
	//int irq_num = 0;

	memset(g_list_dev_info, 0, sizeof(g_list_dev_info));
	for (i = 0; i < DEV_NUM; i++)
	{ //set ecc_enable to 1 by default.
		g_list_dev_info[i].ecc_enable = EN_ECC;
	}

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
/*
	nd = of_find_node_by_path("/amba_pl/my_irq@0"); // ls /proc/device-tree/amba_pl/gpio\@41210000/
	if(NULL == nd) {
		printk(KERN_ERR "Failed to get gpio node at /amba_pl/gpio@41210000\n");
		return -EINVAL;
	}
	irq_num = irq_of_parse_and_map(nd, 0);
	if(0 == irq_num) {
		printk(KERN_ERR "Failed to get irq_num\n");
		return -EINVAL;
	}
	printk("irq_num is %d\n",irq_num);
	
	err = request_irq(irq_num, qspi_isr,IRQF_TRIGGER_RISING| IRQF_ONESHOT,DEVICE_NAME,NULL);
	//err = request_threaded_irq(irq_num, NULL, qspi_isr, IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev_name, NULL);
    if (err)
    {
        printk(KERN_ALERT "request_irq error=%d\n", err);
        return -EINVAL;
    }
    else
    {
        printk("request_irq success ! \n");
    }
*/
	major = register_chrdev(0, "s_qspi", &qspi_drv);

	qspi_class = class_create(THIS_MODULE, "s_qspi_class");
	err = PTR_ERR(qspi_class);
	if (IS_ERR(qspi_class))
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "s_qspi");
		return -1;
	}

	for (i = 0; i < DEV_NUM; i++)
	{
		device_create(qspi_class, NULL, MKDEV(major, i), NULL, "s_qspi_%02d", i); /* /dev/s_qspi_00,01,... */
	}

	return 0;
}

static void __exit qspi_exit(void)
{
	int i;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	for (i = 0; i < DEV_NUM; i++)
		device_destroy(qspi_class, MKDEV(major, i)); /* /dev/s_qspi0,1,... */

	device_destroy(qspi_class, MKDEV(major, 0));
	class_destroy(qspi_class);
	unregister_chrdev(major, "s_qspi");
	for (i = 0; i < DEV_NUM; i++)
	{
		if (g_list_dev_info[i].remap_flag != 0)
		{
			device_close_deinit(i);
		}
	}
	return;
}

module_init(qspi_init);
module_exit(qspi_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sieffi");
MODULE_DESCRIPTION("Sieffi qspi driver");
